package demo.retry;

import com.github.rholder.retry.AttemptTimeLimiter;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.TimeLimiter;

import javax.annotation.concurrent.Immutable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author haijun
 * @version 1.0.0
 * @email "mailto:iamxiaohaijun@gmail.com"
 * @date 2023.07.19 19:14
 * @since 2023.1.1
 */
public final class AttemptTimeLimiters {
    /**
     * Fkh attempt time limiters
     *
     * @since 2023.1.1
     */
    private AttemptTimeLimiters() {
    }

    /**
     * No time limit
     *
     * @param <V> parameter
     * @return the attempt time limiter
     * @since 2023.1.1
     */
    public static <V> AttemptTimeLimiter<V> noTimeLimit() {
        return new AttemptTimeLimiters.NoAttemptTimeLimit<>();
    }


    /**
     * Fixed time limit
     *
     * @param <V>             parameter
     * @param duration        duration
     * @param timeUnit        time unit
     * @param executorService executor service
     * @return the attempt time limiter
     * @since 2023.1.1
     */
    public static <V> AttemptTimeLimiter<V> fixedTimeLimit(long duration, TimeUnit timeUnit,
                                                           ExecutorService executorService) {
        Preconditions.checkNotNull(timeUnit);
        return new FixedAttemptTimeLimit<>(duration, timeUnit, executorService);
    }

    /**
     * @param <V> parameter
     * @author haijun
     * @version 1.0.0
     * @email "mailto:iamxiaohaijun@gmail.com"
     * @date 2023.07.19 19:14
     * @since 2023.1.1
     */
    @Immutable
    private static final class NoAttemptTimeLimit<V> implements AttemptTimeLimiter<V> {
        /**
         * Call
         *
         * @param callable callable
         * @return the v
         * @throws Exception exception
         * @since 2023.1.1
         */
        @Override
        public V call(Callable<V> callable) throws Exception {
            return callable.call();
        }
    }

    /**
     * @param <V> parameter
     * @author haijun
     * @version 1.0.0
     * @email "mailto:iamxiaohaijun@gmail.com"
     * @date 2023.07.19 19:14
     * @since 2023.1.1
     */
    @Immutable
    private static final class FixedAttemptTimeLimit<V> implements AttemptTimeLimiter<V> {

        /**
         * Time limiter
         */
        private final TimeLimiter timeLimiter;
        /**
         * Duration
         */
        private final long duration;
        /**
         * Time unit
         */
        private final TimeUnit timeUnit;


        /**
         * Fixed attempt time limit
         *
         * @param duration        duration
         * @param timeUnit        time unit
         * @param executorService executor service
         * @since 2023.1.1
         */
        FixedAttemptTimeLimit(long duration, TimeUnit timeUnit, ExecutorService executorService) {
            this(new SimpleTimeLimiter(executorService), duration, timeUnit);
        }

        /**
         * Fixed attempt time limit
         *
         * @param timeLimiter time limiter
         * @param duration    duration
         * @param timeUnit    time unit
         * @since 2023.1.1
         */
        private FixedAttemptTimeLimit(TimeLimiter timeLimiter, long duration, TimeUnit timeUnit) {
            Preconditions.checkNotNull(timeLimiter);
            Preconditions.checkNotNull(timeUnit);
            this.timeLimiter = timeLimiter;
            this.duration = duration;
            this.timeUnit = timeUnit;
        }

        /**
         * Call
         *
         * @param callable callable
         * @return the v
         * @throws Exception exception
         * @since 2023.1.1
         */
        @Override
        public V call(Callable<V> callable) throws Exception {
            return this.timeLimiter.callWithTimeout(callable, this.duration, this.timeUnit);
        }
    }
}
